package board;

import character.MyCharacter;
import com.jme3.bullet.control.RigidBodyControl;
import modifier.tile.TileModifier;
import item.Item;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;
import effect.Effect;
import effect.character.single.SingleCharacterEffect;
import modifier.tile.TileModifierSight;
import game.CombatController;
import game.DataStore;
import game.combat.CombatActor;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import misc.Vector3i;

public class Tile extends Node implements CombatActor {
    public static final float SIZE = 1.5f;
    public static final float HEIGHT = 1.5f/2;
    public static final float SIZE_DIAGONAL = 2*FastMath.sqrt(0.5f*FastMath.sqr(SIZE));
    protected Geometry geometry;
    protected Material mat;
    protected Vector3i boardLocation;
    protected Set<MyCharacter> characters = new HashSet<MyCharacter>();
    protected Item item;
    protected boolean discovered = false, visible = false;
    protected Map<TileModifier, TileModifier> tileModifiers = new HashMap<TileModifier, TileModifier>();
    protected Set<Effect> tileWalkEffects = new HashSet<Effect>();
    protected int initiative;
    protected boolean firstTurn = true;

    @Deprecated
    protected Tile() {}
    
    public Tile(Vector3i boardLocation) {
        init(boardLocation);
    }

    private void init(Vector3i location) {
	geometry = new Geometry("Tile", new Box(Tile.SIZE/2, HEIGHT/2, Tile.SIZE/2));
	mat = new Material(DataStore.assetManager, "MatDefs/UnshadedFogOfWar.j3md");
        mat.setColor("Color", ColorRGBA.LightGray);
	mat.setColor("FogColor", ColorRGBA.Black);
        geometry.setMaterial(mat);
	attachChild(geometry);
        
        boardLocation = location;
        
        RigidBodyControl physicsControl = new RigidBodyControl(0f);
        addControl(physicsControl);
        physicsControl.setPhysicsLocation(new Vector3f(boardLocation.getX()*Tile.SIZE+Tile.SIZE/2, 0, boardLocation.getZ()*Tile.SIZE+Tile.SIZE/2));
        
	if(DataStore.DISCOVER_TILES)
	    setDiscovered(false);
	if(DataStore.FOG_OF_WAR)
	    setVisible(false);
    }
    
    public void highlight(HighlightType highlightType) {
        ColorRGBA color = ColorRGBA.LightGray;
        switch(highlightType) {
            case MOVE:
                color = ColorRGBA.Blue;
                break;
            case RANGE:
                color = ColorRGBA.Pink;
                break;
            case WALK_EFFECT:
                color = ColorRGBA.Green;
                break;
        }
        geometry.getMaterial().setColor("Color", color);
    }
    public void highlight(ColorRGBA color) {
        geometry.getMaterial().setColor("Color", color);
    }
    public void unhighlight() {
        geometry.getMaterial().setColor("Color", ColorRGBA.LightGray);
    }
    
    public boolean isDiscovered(){
	return discovered;
    }
    public void setDiscovered(boolean discovered) {
	this.discovered = discovered;
	if(DataStore.DISCOVER_TILES) {
	    if(discovered)
		setCullHint(CullHint.Dynamic);
	    else
		setCullHint(CullHint.Always);
	}
    }
    public boolean isVisible() {
	return visible;
    }
    public void setVisible(boolean visible) {
	this.visible = visible;
	if(visible) {
	    if(DataStore.FOG_OF_WAR)
		mat.setBoolean("Fog", false);
	    if(DataStore.DISCOVER_TILES) {
		for(MyCharacter character : characters)
		    character.setCullHint(CullHint.Dynamic);
		if(item != null)
		    item.setCullHint(CullHint.Dynamic);
	    }
	} else {
	    if(DataStore.FOG_OF_WAR)
		mat.setBoolean("Fog", true);
	    if(DataStore.DISCOVER_TILES) {
		for(MyCharacter character : characters)
		    character.setCullHint(CullHint.Always);
		if(item != null)
		    item.setCullHint(CullHint.Always);
	    }
	}
    }
    
    public Collection<MyCharacter> getCharacters() {
        return characters;
    }
    public boolean containsCharacter(MyCharacter character) {
        return characters.contains(character);
    }
    public boolean addCharacter(MyCharacter character, boolean forceVisible) {
        characters.add(character);
	if(DataStore.FOG_OF_WAR) {
	    if(!forceVisible && (!discovered || !visible)) {
		character.setCullHint(CullHint.Always);
		return false;
	    } else {
		character.setCullHint(CullHint.Dynamic);
		return true;
	    }
	} else {
	    return true;
	}
    }
    public void removeCharacter(MyCharacter character) {
        characters.remove(character);
    }
    public Item getItem() {
        return item;
    }
    public boolean setItem(Item item, boolean forceVisible) {
        this.item = item;
	if(!forceVisible && (!discovered || !visible)) {
	    item.setCullHint(CullHint.Always);
	    return false;
	} else {
	    item.setCullHint(CullHint.Dynamic);
	    return true;
	}
    }
    public void removeItem() {
        item = null;
    }
    public Vector3i getBoardLocation() {
	return boardLocation;
    }
    public void setBoardLocation(Vector3i boardLocation) {
	this.boardLocation = boardLocation;
    }
    
    public Collection<TileModifier> getTileModifiers() {
        return tileModifiers.values();
    }
    public void addTileModifier(TileModifier tileModifier) {
        if(!tileModifiers.containsKey(tileModifier)) {
            TileModifier tileModifierClone = tileModifier.cloneForTile(this);
            tileModifiers.put(tileModifierClone, tileModifierClone);
            tileModifierClone.applyModifier();
        } else if(tileModifiers.containsKey(tileModifier) && tileModifier.getTicksRemaining() >= tileModifiers.get(tileModifier).getTicksRemaining()) {
            System.out.println("Refreshing ticks remaining for "+tileModifier+" to "+tileModifier.getTicksRemaining()+" ticks.");
            tileModifiers.get(tileModifier).setTicksRemaining(tileModifier.getTicksRemaining());
        }
        
    }
    public void addTileModifiers(Collection<TileModifier> tileModifiers) {
        for(TileModifier tileModifier : tileModifiers)
            addTileModifier(tileModifier);
    }
    protected void removeTileModifier(TileModifier tileModifier) {
        tileModifier.abortModifier();
        tileModifiers.remove(tileModifier);
    }

    public boolean hasSightTileModifiers() {
	for(TileModifier tileModifier: tileModifiers.values()) {
	    if(tileModifier instanceof TileModifierSight) {
		return true;
	    }
	}
	return false;
    }
    
    public void addTileWalkEffect(Effect tileWalkEffect) {
        if(tileWalkEffects.contains(tileWalkEffect)) {
            removeTileWalkEffect(tileWalkEffect);
        }
        DataStore.board.highlightBoardLocation(boardLocation, new HighlightRequest(this, HighlightType.WALK_EFFECT));
        tileWalkEffects.add(tileWalkEffect);
        highlight(HighlightType.WALK_EFFECT);
    }
    public void removeTileWalkEffect(Effect tileWalkEffect) {
        tileWalkEffects.remove(tileWalkEffect);
        if(tileWalkEffects.isEmpty()) {
            unhighlight();
        }
    }
    public void triggerTileWalkEffects(MyCharacter triggeringCharacter) {
        for(Effect tileWalkEffect : tileWalkEffects) {
            if(tileWalkEffect instanceof SingleCharacterEffect) {
                ((SingleCharacterEffect)tileWalkEffect).setTarget(triggeringCharacter);
            }
            CombatController.executeEffect(tileWalkEffect);
        }
    }
    
    @Override
    public void turnStarting() {
	tickModifiers();
    }
    
    protected void tickModifiers() {
        boolean removedSightTileModifier = false;
	Iterator<TileModifier> iter = tileModifiers.keySet().iterator();
	while(iter.hasNext()) {
	    TileModifier tileModifier = iter.next();
	    tileModifier.tickModifier();
	    if(tileModifier.isMarkedForRemoval()) {
		iter.remove();
		if(tileModifier instanceof TileModifierSight)
		    removedSightTileModifier = true;
	    }
	}
	if(removedSightTileModifier)
	    DataStore.board.updateVisibleTiles(DataStore.player);
    }
    
    //Implementing the CombatActor interface
    @Override
    public int getInitiative() {
        return initiative;
    }
    @Override
    public void setInitiative(int initiative) {
        this.initiative = initiative;
    }
    @Override
    public boolean isFirstTurn() {
        return firstTurn;
    }
    @Override
    public void setFirstTurn(boolean firstTurn) {
        this.firstTurn = firstTurn;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Tile other = (Tile) obj;
        if (this.boardLocation != other.boardLocation && (this.boardLocation == null || !this.boardLocation.equals(other.boardLocation))) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 37 * hash + (this.boardLocation != null ? this.boardLocation.hashCode() : 0);
        return hash;
    }
}
